package csm.common.security;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import csm.common.net.EthernetAddress;

public final class GUIDGenerator
{
	private static final GUIDGenerator sSingleton = new GUIDGenerator();

	private SecureRandom mRnd;
	private final Object mDummyAddressLock = new Object();
	private EthernetAddress mDummyAddress;
	private final Object mTimerLock = new Object();
	private GUIDTimer mTimer;
	private MessageDigest mHasher;

	private GUIDGenerator()
	{
		mRnd = null;
		mDummyAddress = null;
		mTimer = null;
		mHasher = null;
	}

	public static GUIDGenerator getInstance()
	{
		return sSingleton;
	}

	public EthernetAddress getDummyAddress()
	{

		synchronized (mDummyAddressLock)
		{
			if (mDummyAddress == null)
			{
				SecureRandom rnd = getRandomNumberGenerator();
				byte dummy[] = new byte[6];
				rnd.nextBytes(dummy);
				dummy[0] |= 0x80;

				try
				{
					mDummyAddress = new EthernetAddress(dummy);
				}
				catch (NumberFormatException numberformatexception)
				{
				}
			}
		}
		return mDummyAddress;
	}

	public SecureRandom getRandomNumberGenerator()
	{
		if (mRnd == null) mRnd = new SecureRandom();

		return mRnd;
	}

	public MessageDigest getHashAlgorithm()
	{
		if (mHasher == null) try
		{
			mHasher = MessageDigest.getInstance("MD5");
		}
		catch (NoSuchAlgorithmException nex)
		{
			throw new Error("Couldn't instantiate an MD5 MessageDigest instance: " + nex.toString());
		}
		return mHasher;
	}

	public GUID generateRandomBasedUUID()
	{
		return generateRandomBasedUUID(getRandomNumberGenerator());
	}

	public GUID generateRandomBasedUUID(SecureRandom randomGenerator)
	{
		byte rnd[] = new byte[16];
		randomGenerator.nextBytes(rnd);
		return new GUID(4, rnd);
	}

	public GUID generateTimeBasedUUID()
	{
		return generateTimeBasedUUID(getDummyAddress());
	}

	public GUID generateTimeBasedUUID(EthernetAddress addr)
	{
		byte contents[] = new byte[16];
		addr.toByteArray(contents, 10);

		synchronized (mTimerLock)
		{
			if (mTimer == null) mTimer = new GUIDTimer(getRandomNumberGenerator());
			mTimer.getTimestamp(contents);
		}
		return new GUID(1, contents);
	}

	public GUID generateNameBasedUUID(GUID nameSpaceUUID, String name, MessageDigest digest)
	{

		digest.reset();

		if (nameSpaceUUID != null) digest.update(nameSpaceUUID.asByteArray());

		digest.update(name.getBytes());
		return new GUID(3, digest.digest());
	}

	public GUID generateNameBasedUUID(GUID nameSpaceUUID, String name)
	{

		MessageDigest hasher = getHashAlgorithm();

		synchronized (hasher)
		{
			return generateNameBasedUUID(nameSpaceUUID, name, getHashAlgorithm());
		}
	}

	public static void main(String[] args)
	{
		GUIDGenerator gen = GUIDGenerator.getInstance();
		GUID uuid = gen.generateRandomBasedUUID();
		System.out.println(uuid.toString());
	}
}
